package cfiles

import (
	"bufio"
	"bytes"
	"gomeshsync/cfile"
	"gomeshsync/config"
	"gomeshsync/consts"
	"log"
	"net"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"gitee.com/kimsoso/go-utils/sonet"
	"gitee.com/kimsoso/go-utils/utils"
)

// 分割文件
func SplitFileToBlocks(file *cfile.Cfile) (blocks []CBlock) {
	bm, stat := cfile.BitMapStat(file.FileMask)
	if stat == cfile.BitStatFull {
		return
	}

	var i uint32
	for i = 0; i < bm.Max(); i++ {
		if !bm.IsExist(uint(i)) {
			blocksize := file.BlockSize

			//最后一块，并且不到一个整块的大小
			if i == bm.Max()-1 && consts.BLOCKSIZE*(i+1) > uint32(file.FileSize) {
				blocksize = file.FileSize - blocksize*int64(i)
			}
			blocks = append(blocks, CBlock{seq: int(i), offset: int64(i) * file.BlockSize, size: blocksize, filePath: file.FilePath})
		}
	}

	return
}

// 创建一个文件块
func MakeBlocks(filePath, fileMask string, fileSize, blockSize int64) (blocks []CBlock) {
	if !strings.Contains(fileMask, "0") {
		return
	}
	maskLen := len(fileMask)
	for i := 0; i < maskLen; i++ {
		if fileMask[i:i+1] == "0" {
			blocksize := blockSize

			//最后一块，并且不到一个整块的大小
			if i == maskLen-1 && consts.BLOCKSIZE*(i+1) > int(fileSize) {
				blocksize = fileSize - blocksize*int64(i)
			}
			blocks = append(blocks, CBlock{seq: i, offset: int64(i) * blockSize, size: blocksize, filePath: filePath})
		}
	}
	return
}

/**
 * @description: 接收从客户端发出的文件数据流
 * @param {net.Conn} conn socket连接
 * @param {string} filePath 需要写入的数据文件路径
 * @param {int64} offset 需要的文件偏移量
 * @return {*}
 */
func receiveFileData(conn net.Conn, filePath string, remoteFileSize, offset int64) error {
	dstFile, err := os.OpenFile(filePath, os.O_WRONLY, os.ModePerm)
	if err != nil {
		return err
	}

	_, err = dstFile.Seek(offset, 0)
	if err != nil {
		return err
	}

	bufWriter := bufio.NewWriter(dstFile)

	defer func() {
		bufWriter.Flush()
		dstFile.Close()
	}()

	var nWrote int64 = offset
	return sonet.ReceiveLongData(conn, func(buf []byte) bool {
		nw, _ := bufWriter.Write(buf)
		nWrote += int64(nw)
		if nWrote >= remoteFileSize {
			log.Println("received:", nWrote, " remote file size is:", remoteFileSize, " file is:", filePath)
			return false
		}
		return true
	})
}

/**
 * @description: 解析新文件参数
 * @param {[]byte} command
 * @return {*} filename, filesize
 */
func parseNewFileParams(command []byte) (isDir bool, filePath string, fileSize int64) {
	params := bytes.Split(command, []byte(":"))
	filePath = string(params[2])
	size, _ := strconv.Atoi(string(params[3]))
	isDir = string(params[4]) == "1"
	fileSize = int64(size)
	return
}

// 对比本地路径结构
func compairWithLocalFiles(targetDir string, syncFiles *map[string]*Ufile) {
	//删除本地无用文件，并写入本地命令序列
	localTargetPath := toRealSvrPath(targetDir)
	localFiles := utils.GetAllFiles(localTargetPath)

	// 远端不存在而本地存在，则删除本地文件
	for _, lfile := range localFiles {
		if strings.HasSuffix(lfile, ".ctfm") {
			continue
		}
		_relPath, _ := filepath.Rel(config.DirFiles, lfile)
		relPath := filepath.Clean(_relPath)

		_, ok := (*syncFiles)[relPath]
		if !ok {
			SetDeleteFileCommand(relPath)
			os.RemoveAll(lfile)
		}
	}

	var realPath string

	for k, sfile := range *syncFiles {
		realPath = toRealSvrPath(targetDir, sfile.Path)
		_, err := os.Stat(realPath)

		// 如果本地文件存在并大小(md5)相同，删除该条记录
		if err == nil {
			if sfile.FileType == cfile.IsFile {
				// fmd5, _ := utils.HashFileMd5(realPath)
				fi, _ := os.Stat(realPath)
				if fi.Size() == sfile.FileSize {
					delete(*syncFiles, k)
				}
			} else {
				delete(*syncFiles, k)
			}
		}
	}
}
